/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>

#include "imgops/thresholding/functions/thresholding.h"
#include "prj_definitions.h"

using namespace std;
using namespace cv;

namespace NAMESPACE{

void histeresys_search(const Mat& padded_in, Mat& padded_out,
                       MatConstIterator_<uchar> in_it,
                       uchar t_min, uchar t_max, uchar v_max,
                       int block_size){
    int pad_add = std::floor(block_size / 2);
    Point p = in_it.pos();

    const uchar* in_pad_ptr = padded_in.ptr(p.y, p.x);
    uchar* out_pad_ptr = padded_out.ptr(p.y, p.x);

    int cols = padded_in.cols;
    int not_padded_cols = cols - block_size + 1;

    int row;
    uchar val;
    MatConstIterator_<uchar> forward_it;
    for(int i = 0; i < block_size; i++){
        row = i * cols;
        for(int j = 0; j < block_size; j++){
            val = *(in_pad_ptr + row + j);
            if( val > t_min && val < t_max && *(out_pad_ptr + row + j) < v_max){
                *(out_pad_ptr + row + j) = v_max;
                forward_it = in_it + ( (-pad_add + i) * not_padded_cols ) + (-pad_add + j);
                histeresys_search(padded_in, padded_out, forward_it, t_min, t_max, v_max, block_size);
            }
        }
    }
}

void hysteresis_thresh(const Mat& in, Mat& out, uchar t1, uchar t2, uchar v1, uchar v2, int pad_dim){
    int rows = in.rows, cols = in.cols;
    int pad_add = std::floor(pad_dim / 2);

    uchar t_min  = min(t1, t2), t_max = max(t1, t2);
    uchar _false = min(v1, v2), _true = max(v1, v2);

    Mat tmp_out(rows, cols, DataType<uchar>::type, _false);
    tmp_out.colRange(0, pad_add)               = _true;
    tmp_out.colRange(cols - pad_add - 1, cols) = _true;
    tmp_out.rowRange(0, pad_add)               = _true;
    tmp_out.rowRange(rows - pad_add - 1, rows) = _true;
    Mat sub_in(in, Rect(pad_add, pad_add, cols - pad_add - 1, rows - pad_add - 1));
    Mat sub_out(tmp_out, Rect(pad_add, pad_add, cols - pad_add - 1, rows - pad_add - 1));

    MatConstIterator_<uchar> in_it = sub_in.begin<uchar>(), in_end_it = sub_in.end<uchar>();
    MatIterator_<uchar> out_it = sub_out.begin<uchar>();

    for(; in_it != in_end_it; in_it++, out_it++){
        if(*in_it > t_max){
            *out_it = _true;
            histeresys_search(in, tmp_out, in_it, t_min, t_max, _true, pad_dim);
        }
        else if(*in_it <= t_min) *out_it = _false;
    }
    out = tmp_out;
}

}
